# -*- coding: utf-8 -*-
import os
import threading


def clone_table(table, name, metadata=None):
    from sqlalchemy import Table
    if not metadata:
        metadata = table.metadata
    try:
        return metadata.tables[name]
    except KeyError:
        args = []
        kwargs = {}
        args.append(name)
        args.append(metadata)
        for c in table.columns:
            args.append(c.copy())
        for c in table.constraints:
            args.append(c.copy())
        return Table(*args)


class QuerySet(object):
    def __init__(self, result):
        self.result = result

    def __getslice__(self, x, y):
        return self.result.limit(y-x).offset(x).execute().fetchall()

    def count(self):
        from sqlalchemy import func, select
        return select([func.count()]).select_from(self.result.alias()).execute().scalar()

    def __len__(self):
        return self.count()


class Result(object):
    def __init__(self, lst):
        self.lst = lst

    def get_one(self):
        if not self.lst:
            return None
        return self.lst[0]

    @staticmethod
    def set_variable(variable, value):
        variable.set(value, from_db=True)

    def __iter__(self):
        for row in self.lst:
            yield row

try:
    from storm.locals import create_database
    from storm.locals import Store
    from storm.store import ResultSet
    from storm import Undef
    from storm.exceptions import StormError, NotOneError
    class CacheResultSet(ResultSet):
        def fetchAll(self, cache, key, timeout=0):
            values_list = cache.get(key)
            if values_list is None:
                result = self._store._connection.execute(self._get_select())
                values_list = list(result)
                cache.set(key, values_list, timeout)
            else:
                result = Result(values_list)
            return [self._load_objects(result, values) for values in values_list]
        def fetchOne(self, cache, key, timeout=0):
            values = cache.get(key)
            if values is None:
                select = self._get_select()
                if select.limit is not Undef and select.limit > 2:
                    select.limit = 2
                result = self._store._connection.execute(select)
                values = result.get_one()
                if result.get_one():
                    raise NotOneError("one() used with more than one result available")
                cache.set(key, values, timeout)
            else:
                result = Result([values])
            if values:
                return self._load_objects(result, values)
            return None
    class CacheStore(Store):
        _result_set_factory = CacheResultSet
except ImportError:
    pass

class ThreadSafeStore(object):
    def __init__(self, uri, cache=None):
        #self.__uri = uri
        self.__db = create_database(uri)
        self.__local = threading.local()
        self.__cache = cache

    def _getStore(self):
        local = self.__local
        if hasattr(local, 'store'):
            return local.store
        else:
            local.store = CacheStore(self.__db)
            local.store.execute('Set AUTOCOMMIT = 0')
            return local.store

    def __getattr__(self, name):
        store = self._getStore()
        return getattr(store, name)

    def showTables(self):
        for table in self.execute('SHOW TABLES'):
            print table[0]

    def dropTables(self):
        tables = [table[0] for table in self.execute('SHOW TABLES')]
        for table in tables:
            self.execute('drop table if exists %s' % table)

    def showCreate(self):
        for table in self.execute('SHOW TABLES'):
            for line in self.execute('SHOW CREATE TABLE `%s`' % table[0]):
                print line[1]
                print

    def runDBShell(self):
        kwargs = self.__db._connect_kwargs
        os.system('mysql -u %s --password=%s -h %s -P %s -D %s' % (
            kwargs['user'], kwargs['passwd'], kwargs['host'],
            kwargs['port'], kwargs['db'])
        )

    def new(self):
        self.rollback()
        return self

